/**
 * Copyright (c) 2010 Basil Shikin, BugStat Project
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.bugstat.exporters;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is used to manage storages for bug export process 
 *
 * @author Basil Shikin
 *
 */
public class StorageManager
{
    private static Log log = LogFactory.getLog( StorageManager.class );
    
    public static final String FOLDER_RECORDS   = "records";
    public static final String FOLDER_OBJECTS   = "objects";
    
    private static final String RECORD_FILE_NAME_FORMAT = "yyyy-MM-dd";

    protected static final FilenameFilter s_temporalFileFilter = new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name)
        {
            return name.endsWith(".tmp");
        }
    };
    
    protected static final DateFormat s_recordFileFormat = new SimpleDateFormat( RECORD_FILE_NAME_FORMAT );
    
    protected final File baseFolder;
    protected File[] recordsFiles;
    
    /**
     * Create new storage manager
     * 
     * @param baseFolder Folder to save information under. Must not be null.
     */
    public StorageManager(File baseFolder)
    {
        if ( baseFolder == null ) throw new IllegalArgumentException("Invalid base folder specified");
        
        this.baseFolder = baseFolder;
    }

    public RecordStorage createRecordStorage( int headerLength )
    {
        return new RecordStorage( headerLength );
    }
    
    public void saveRecordStorage( final RecordStorage recordStorage, final Date timestamp ) throws IOException
    {
        if ( recordStorage == null ) throw new IllegalArgumentException("Invalid record storage specified");
        if ( timestamp == null )     throw new IllegalArgumentException("Invalid timestamp specified");
        
        final File recordsFolder = new File( baseFolder, FOLDER_RECORDS );
        if ( !recordsFolder.exists() ) recordsFolder.mkdirs();
        
        final File storageFile = new File( recordsFolder, s_recordFileFormat.format( timestamp ) + ".tmp" );
        
        log.debug("Saving record storage " + storageFile.getAbsolutePath() + "...");
        
        recordStorage.saveRecords( storageFile );
        
        log.debug("Record storage saved to " + storageFile.getAbsolutePath() );
    }
    
    public File[] listTemporalFiles()
    {
        final File recordsFolder = new File( baseFolder, FOLDER_RECORDS );
        recordsFiles = recordsFolder.listFiles( s_temporalFileFilter );
        if ( recordsFiles == null ) recordsFiles = new File[0];
        
        Arrays.sort( recordsFiles );
        
        return recordsFiles;
    }
    
    public void savePermStorage( final RecordStorage recordStorage, final String storageName ) throws IOException
    {
        if ( recordStorage == null ) throw new IllegalArgumentException("Invalid record storage specified");
        
        final File recordsFolder = new File( baseFolder, FOLDER_RECORDS );
        if ( !recordsFolder.exists() ) recordsFolder.mkdirs();
        
        final File storageFile = new File( recordsFolder, storageName + ".perm" );
        
        log.debug("Saving record storage " + storageFile.getAbsolutePath() + "...");
        
        recordStorage.saveRecords( storageFile );
        
        log.debug("Record storage saved to " + storageFile.getAbsolutePath() );
    }
    
    public void loadPermStorage(RecordStorage recordStorage, String storageName) throws IOException
    {
        if ( recordStorage == null ) throw new IllegalArgumentException("Invalid record storage specified");
        
        final File recordsFolder = new File( baseFolder, FOLDER_RECORDS );
        if ( !recordsFolder.exists() ) recordsFolder.mkdirs();
        
        final File storageFile = new File( recordsFolder, storageName + ".perm" );
        
        log.debug("Loading permanent record storage " + storageFile.getAbsolutePath() + "...");
        
        recordStorage.loadRecords( storageFile );
        
        log.debug("Permanent record storage loaded from " + storageFile.getAbsolutePath() );
    }

    public void saveReferenceStorage(ReferenceStorage<?> storage) throws IOException
    {
        if ( storage == null ) throw new IllegalArgumentException("Invalid storage specified");
        
        final File objectsFolder = new File( baseFolder, FOLDER_OBJECTS );
        if ( !objectsFolder.exists() ) objectsFolder.mkdirs();
        
        final File storageFile = new File( objectsFolder, storage.getName() + ".ref" );
        
        log.debug("Saving object storage " + storageFile.getAbsolutePath() + "...");
        
        storage.saveReferences(storageFile);
        
        log.debug("Object storage saved to " + storageFile.getAbsolutePath() );
    }

    public void loadReferenceStorage(ReferenceStorage<?> storage) throws IOException
    {
        if ( storage == null ) throw new IllegalArgumentException("Invalid storage specified");
        
        final File objectsFolder = new File( baseFolder, FOLDER_OBJECTS );
        if ( !objectsFolder.exists() ) objectsFolder.mkdirs();
        
        final File storageFile = new File( objectsFolder, storage.getName() + ".ref" );
        
        log.debug("Loading object storage " + storageFile.getAbsolutePath() + "...");
        
        storage.loadReferences( storageFile );
        
        log.debug("Object storage loaded from " + storageFile.getAbsolutePath() );        
    }
    
    /**
     * Get storage location
     * 
     * @return Storage location
     */
    public File getLocation()
    {
        return baseFolder;
    }
    
    public static File nextTemporalFile( final File[] files, final Date date)
    {
        // TODO : This could be optimized to used modified binary search
        final String targetFileName = s_recordFileFormat.format( date ) + ".tmp";
        
        for ( File file : files )
        {
            final String name = file.getName();
            if ( name.compareTo( targetFileName ) >= 0 )
            {
                return file;
            }
        }
        
        throw new IllegalStateException("No file for " + date );
    }

    
}
